// Go作者版权所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

// +构建忽略

package main

import "strings"

// 笔记：
// -整数类型位于寄存器的低位。上部是垃圾。
// -布尔类型使用寄存器的低位字节。0=假，1=真。
// 上面的字节是垃圾。
// -生长素的未使用部分由延长使用部分的标志填充。
// -*常量指令可能使用大于指令编码的常量。
// 在这种情况下，汇编程序扩展到多条指令并使用tmp
// 寄存器（R23）。

// 后缀对各种指令的位宽度进行编码。
// W（字）=32位
// H（半字）=16位
// HU=16位无符号
// B（字节）=8位
// BU=8位无符号
// F（浮点）=32位浮点
// D（双）=64位浮点

// 注：regalloc中未使用的寄存器不包括在此列表中，
// 所以regmask保持在int64内
// 手工编码时要小心。
var regNamesMIPS = []string{
	"R0", // 常数0
	"R1",
	"R2",
	"R3",
	"R4",
	"R5",
	"R6",
	"R7",
	"R8",
	"R9",
	"R10",
	"R11",
	"R12",
	"R13",
	"R14",
	"R15",
	"R16",
	"R17",
	"R18",
	"R19",
	"R20",
	"R21",
	"R22",
	// REGTMP
	"R24",
	"R25",
	// R26由内核保留
	// R27由内核保留
	"R28",
	"SP",  // 又名R29
	"g",   // 又名R30
	"R31", // REGLINK

	// 奇数FP寄存器包含64位FP值的高位部分
	"F0",
	"F2",
	"F4",
	"F6",
	"F8",
	"F10",
	"F12",
	"F14",
	"F16",
	"F18",
	"F20",
	"F22",
	"F24",
	"F26",
	"F28",
	"F30",

	"HI", // 乘法高位
	"LO", // 乘法低位

	// 如果添加寄存器，请在运行时更新asyncPreempt。

	// 伪寄存器
	"SB",
}

func init() {
	// 将reg名称映射到reg整数。
	if len(regNamesMIPS) > 64 {
		panic("too many registers")
	}
	num := map[string]int{}
	for i, name := range regNamesMIPS {
		num[name] = i
	}
	buildReg := func(s string) regMask {
		m := regMask(0)
		for _, r := range strings.Split(s, " ") {
			if n, ok := num[r]; ok {
				m |= regMask(1) << uint(n)
				continue
			}
			panic("register " + r + " not found")
		}
		return m
	}

	// 通用单个寄存器掩码
	var (
		gp         = buildReg("R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R24 R25 R28 R31")
		gpg        = gp | buildReg("g")
		gpsp       = gp | buildReg("SP")
		gpspg      = gpg | buildReg("SP")
		gpspsbg    = gpspg | buildReg("SB")
		fp         = buildReg("F0 F2 F4 F6 F8 F10 F12 F14 F16 F18 F20 F22 F24 F26 F28 F30")
		lo         = buildReg("LO")
		hi         = buildReg("HI")
		callerSave = gp | fp | lo | hi | buildReg("g") // runtime.setg（以及任何调用它的东西）可能会重击g
		r1         = buildReg("R1")
		r2         = buildReg("R2")
		r3         = buildReg("R3")
		r4         = buildReg("R4")
		r5         = buildReg("R5")
	)
	// 公共区域信息
	var (
		gp01      = regInfo{inputs: nil, outputs: []regMask{gp}}
		gp11      = regInfo{inputs: []regMask{gpg}, outputs: []regMask{gp}}
		gp11sp    = regInfo{inputs: []regMask{gpspg}, outputs: []regMask{gp}}
		gp21      = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}}
		gp31      = regInfo{inputs: []regMask{gp, gp, gp}, outputs: []regMask{gp}}
		gp2hilo   = regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{hi, lo}}
		gpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}
		gpstore   = regInfo{inputs: []regMask{gpspsbg, gpg}}
		gpxchg    = regInfo{inputs: []regMask{gpspsbg, gpg}, outputs: []regMask{gp}}
		gpcas     = regInfo{inputs: []regMask{gpspsbg, gpg, gpg}, outputs: []regMask{gp}}
		gpstore0  = regInfo{inputs: []regMask{gpspsbg}}
		fp01      = regInfo{inputs: nil, outputs: []regMask{fp}}
		fp11      = regInfo{inputs: []regMask{fp}, outputs: []regMask{fp}}
		fp21      = regInfo{inputs: []regMask{fp, fp}, outputs: []regMask{fp}}
		fp2flags  = regInfo{inputs: []regMask{fp, fp}}
		fpload    = regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{fp}}
		fpstore   = regInfo{inputs: []regMask{gpspsbg, fp}}
		readflags = regInfo{inputs: nil, outputs: []regMask{gp}}
	)
	ops := []opData{
		{name: "ADD", argLength: 2, reg: gp21, asm: "ADDU", commutative: true},                                                                           // arg0+arg1
		{name: "ADDconst", argLength: 1, reg: gp11sp, asm: "ADDU", aux: "Int32"},                                                                         // arg0+生长素
		{name: "SUB", argLength: 2, reg: gp21, asm: "SUBU"},                                                                                              // arg0-arg1
		{name: "SUBconst", argLength: 1, reg: gp11, asm: "SUBU", aux: "Int32"},                                                                           // arg0-生长素
		{name: "MUL", argLength: 2, reg: regInfo{inputs: []regMask{gpg, gpg}, outputs: []regMask{gp}, clobbers: hi | lo}, asm: "MUL", commutative: true}, // arg0*arg1
		{name: "MULT", argLength: 2, reg: gp2hilo, asm: "MUL", commutative: true, typ: "(Int32,Int32)"},                                                  // arg0*arg1，有符号，结果高，低
		{name: "MULTU", argLength: 2, reg: gp2hilo, asm: "MULU", commutative: true, typ: "(UInt32,UInt32)"},                                              // arg0*arg1，无符号，结果高，低
		{name: "DIV", argLength: 2, reg: gp2hilo, asm: "DIV", typ: "(Int32,Int32)"},                                                                      // arg0/arg1，已签名，结果hi=arg0%arg1，lo=arg0/arg1
		{name: "DIVU", argLength: 2, reg: gp2hilo, asm: "DIVU", typ: "(UInt32,UInt32)"},                                                                  // arg0/arg1，已签名，结果hi=arg0%arg1，lo=arg0/arg1

		{name: "ADDF", argLength: 2, reg: fp21, asm: "ADDF", commutative: true}, // arg0+arg1
		{name: "ADDD", argLength: 2, reg: fp21, asm: "ADDD", commutative: true}, // arg0+arg1
		{name: "SUBF", argLength: 2, reg: fp21, asm: "SUBF"},                    // arg0-arg1
		{name: "SUBD", argLength: 2, reg: fp21, asm: "SUBD"},                    // arg0-arg1
		{name: "MULF", argLength: 2, reg: fp21, asm: "MULF", commutative: true}, // arg0*arg1
		{name: "MULD", argLength: 2, reg: fp21, asm: "MULD", commutative: true}, // arg0*arg1
		{name: "DIVF", argLength: 2, reg: fp21, asm: "DIVF"},                    // arg0/arg1
		{name: "DIVD", argLength: 2, reg: fp21, asm: "DIVD"},                    // arg0/arg1

		{name: "AND", argLength: 2, reg: gp21, asm: "AND", commutative: true},                // arg0和arg1
		{name: "ANDconst", argLength: 1, reg: gp11, asm: "AND", aux: "Int32"},                // arg0&生长素
		{name: "OR", argLength: 2, reg: gp21, asm: "OR", commutative: true},                  // arg0 | arg1
		{name: "ORconst", argLength: 1, reg: gp11, asm: "OR", aux: "Int32"},                  // arg0 |生长素
		{name: "XOR", argLength: 2, reg: gp21, asm: "XOR", commutative: true, typ: "UInt32"}, // arg0^arg1
		{name: "XORconst", argLength: 1, reg: gp11, asm: "XOR", aux: "Int32", typ: "UInt32"}, // arg0^生长素
		{name: "NOR", argLength: 2, reg: gp21, asm: "NOR", commutative: true},                // ^（arg0 | arg1）
		{name: "NORconst", argLength: 1, reg: gp11, asm: "NOR", aux: "Int32"},                // ^（arg0 |生长素）

		{name: "NEG", argLength: 1, reg: gp11},                 // -arg0
		{name: "NEGF", argLength: 1, reg: fp11, asm: "NEGF"},   // -arg0，32
		{name: "NEGD", argLength: 1, reg: fp11, asm: "NEGD"},   // -arg0，64
		{name: "SQRTD", argLength: 1, reg: fp11, asm: "SQRTD"}, // sqrt（arg0），浮点64
		{name: "SQRTF", argLength: 1, reg: fp11, asm: "SQRTF"}, // sqrt（arg0），浮动32

		// 转移
		{name: "SLL", argLength: 2, reg: gp21, asm: "SLL"},                    // arg0<<arg1，移位量为mod 32
		{name: "SLLconst", argLength: 1, reg: gp11, asm: "SLL", aux: "Int32"}, // arg0<<生长素，移位量必须为0到31（包括0到31
		{name: "SRL", argLength: 2, reg: gp21, asm: "SRL"},                    // arg0>>arg1，无符号，移位量为mod 32
		{name: "SRLconst", argLength: 1, reg: gp11, asm: "SRL", aux: "Int32"}, // arg0>>生长素，移位量必须为0到31（含）
		{name: "SRA", argLength: 2, reg: gp21, asm: "SRA"},                    // arg0>>arg1，已签名，移位量为mod 32
		{name: "SRAconst", argLength: 1, reg: gp11, asm: "SRA", aux: "Int32"}, // arg0>>生长素，已签名，移位量必须为0到31（含0到31）

		{name: "CLZ", argLength: 1, reg: gp11, asm: "CLZ"},

		// 比较
		{name: "SGT", argLength: 2, reg: gp21, asm: "SGT", typ: "Bool"},                      // 如果arg0>arg1（已签名），则为1，否则为0
		{name: "SGTconst", argLength: 1, reg: gp11, asm: "SGT", aux: "Int32", typ: "Bool"},   // 如果生长素>arg0（有符号），则为1，否则为0
		{name: "SGTzero", argLength: 1, reg: gp11, asm: "SGT", typ: "Bool"},                  // 如果arg0>0（已签名），则为1，否则为0
		{name: "SGTU", argLength: 2, reg: gp21, asm: "SGTU", typ: "Bool"},                    // 如果arg0>arg1（无符号），则为1，否则为0
		{name: "SGTUconst", argLength: 1, reg: gp11, asm: "SGTU", aux: "Int32", typ: "Bool"}, // 如果生长素>arg0（无符号），则为1，否则为0
		{name: "SGTUzero", argLength: 1, reg: gp11, asm: "SGTU", typ: "Bool"},                // 如果arg0>0（无符号），则为1，否则为0

		{name: "CMPEQF", argLength: 2, reg: fp2flags, asm: "CMPEQF", typ: "Flags"}, // 如果arg0=arg1，则flags=true，浮动32
		{name: "CMPEQD", argLength: 2, reg: fp2flags, asm: "CMPEQD", typ: "Flags"}, // 如果arg0=arg1，则flags=true，浮点64
		{name: "CMPGEF", argLength: 2, reg: fp2flags, asm: "CMPGEF", typ: "Flags"}, // 如果arg0>=arg1，则flags=true，浮动32
		{name: "CMPGED", argLength: 2, reg: fp2flags, asm: "CMPGED", typ: "Flags"}, // 如果arg0>=arg1，则flags=true，浮点64
		{name: "CMPGTF", argLength: 2, reg: fp2flags, asm: "CMPGTF", typ: "Flags"}, // 如果arg0>arg1，则flags=true，浮动32
		{name: "CMPGTD", argLength: 2, reg: fp2flags, asm: "CMPGTD", typ: "Flags"}, // 如果arg0>arg1，则flags=true，浮点64

		// 移动
		{name: "MOVWconst", argLength: 0, reg: gp01, aux: "Int32", asm: "MOVW", typ: "UInt32", rematerializeable: true},    // 生长素
		{name: "MOVFconst", argLength: 0, reg: fp01, aux: "Float32", asm: "MOVF", typ: "Float32", rematerializeable: true}, // 生长素为64位浮点，转换为32位浮点
		{name: "MOVDconst", argLength: 0, reg: fp01, aux: "Float64", asm: "MOVD", typ: "Float64", rematerializeable: true}, // 生长素作为64位浮点

		{name: "MOVWaddr", argLength: 1, reg: regInfo{inputs: []regMask{buildReg("SP") | buildReg("SB")}, outputs: []regMask{gp}}, aux: "SymOff", asm: "MOVW", rematerializeable: true, symEffect: "Addr"}, // arg0+auxInt+aux（*gc.Sym），arg0=SP/SB

		{name: "MOVBload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVB", typ: "Int8", faultOnNilArg0: true, symEffect: "Read"},     // 从arg0+auxInt+aux加载。arg1=mem。
		{name: "MOVBUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVBU", typ: "UInt8", faultOnNilArg0: true, symEffect: "Read"},  // 从arg0+auxInt+aux加载。arg1=mem。
		{name: "MOVHload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVH", typ: "Int16", faultOnNilArg0: true, symEffect: "Read"},    // 从arg0+auxInt+aux加载。arg1=mem。
		{name: "MOVHUload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVHU", typ: "UInt16", faultOnNilArg0: true, symEffect: "Read"}, // 从arg0+auxInt+aux加载。arg1=mem。
		{name: "MOVWload", argLength: 2, reg: gpload, aux: "SymOff", asm: "MOVW", typ: "UInt32", faultOnNilArg0: true, symEffect: "Read"},   // 从arg0+auxInt+aux加载。arg1=mem。
		{name: "MOVFload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVF", typ: "Float32", faultOnNilArg0: true, symEffect: "Read"},  // 从arg0+auxInt+aux加载。arg1=mem。
		{name: "MOVDload", argLength: 2, reg: fpload, aux: "SymOff", asm: "MOVD", typ: "Float64", faultOnNilArg0: true, symEffect: "Read"},  // 从arg0+auxInt+aux加载。arg1=mem。

		{name: "MOVBstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将arg1的1字节存储到arg0+auxInt+aux。arg2=mem。
		{name: "MOVHstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将arg1的2个字节存储到arg0+auxInt+aux。arg2=mem。
		{name: "MOVWstore", argLength: 3, reg: gpstore, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将arg1的4个字节存储到arg0+auxInt+aux。arg2=mem。
		{name: "MOVFstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVF", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将arg1的4个字节存储到arg0+auxInt+aux。arg2=mem。
		{name: "MOVDstore", argLength: 3, reg: fpstore, aux: "SymOff", asm: "MOVD", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将8字节的arg1存储到arg0+auxInt+aux。arg2=mem。

		{name: "MOVBstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVB", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将0的1字节存储到arg0+auxInt+aux。arg1=mem。
		{name: "MOVHstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVH", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将2个字节的零存储到arg0+auxInt+aux。arg1=mem。
		{name: "MOVWstorezero", argLength: 2, reg: gpstore0, aux: "SymOff", asm: "MOVW", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // 将4个字节的零存储到arg0+auxInt+aux。arg1=mem。

		// 转换
		{name: "MOVBreg", argLength: 1, reg: gp11, asm: "MOVB"},   // 从arg0移动，从字节扩展符号
		{name: "MOVBUreg", argLength: 1, reg: gp11, asm: "MOVBU"}, // 从arg0移动，从字节扩展为无符号
		{name: "MOVHreg", argLength: 1, reg: gp11, asm: "MOVH"},   // 从arg0开始移动，符号从一半延伸
		{name: "MOVHUreg", argLength: 1, reg: gp11, asm: "MOVHU"}, // 从arg0开始移动，从一半开始无符号扩展
		{name: "MOVWreg", argLength: 1, reg: gp11, asm: "MOVW"},   // 从arg0移动

		{name: "MOVWnop", argLength: 1, reg: regInfo{inputs: []regMask{gp}, outputs: []regMask{gp}}, resultInArg0: true}, // 否，返回同一寄存器中的arg0

		// 条件移动为零（如果arg2为0，则返回arg1，否则返回arg0）
		// 参数顺序颠倒，因此我们可以使用resultInArg0（OpCMOVZ result arg1 arg2->CMOVZ arg2reg，arg1reg，resultReg）
		{name: "CMOVZ", argLength: 3, reg: gp31, asm: "CMOVZ", resultInArg0: true},
		{name: "CMOVZzero", argLength: 2, reg: regInfo{inputs: []regMask{gp, gpg}, outputs: []regMask{gp}}, asm: "CMOVZ", resultInArg0: true},

		{name: "MOVWF", argLength: 1, reg: fp11, asm: "MOVWF"},     // int32->float32
		{name: "MOVWD", argLength: 1, reg: fp11, asm: "MOVWD"},     // int32->float64
		{name: "TRUNCFW", argLength: 1, reg: fp11, asm: "TRUNCFW"}, // float32->int32
		{name: "TRUNCDW", argLength: 1, reg: fp11, asm: "TRUNCDW"}, // 浮点64->int32
		{name: "MOVFD", argLength: 1, reg: fp11, asm: "MOVFD"},     // 浮动32->浮动64
		{name: "MOVDF", argLength: 1, reg: fp11, asm: "MOVDF"},     // 浮动64->浮动32

		// 函数调用
		{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                                               // 调用静态函数aux。（*obj.LSym）。arg0=mem，auxint=argsize，返回mem
		{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{gpsp, buildReg("R22"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // 通过闭包调用函数。arg0=codeptr，arg1=closure，arg2=mem，auxint=argsize，返回mem
		{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{gp}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true},                         // 通过指针调用fn。arg0=codeptr，arg1=mem，auxint=argsize，返回mem

		// 原子行动

		// 从arg0加载。arg1=mem。
		// 返回<value，memory>，以便可以与其他加载一起正确排序。
		// 同步
		// MOV（B|W）（Rarg0），路由
		// 同步
		{name: "LoweredAtomicLoad8", argLength: 2, reg: gpload, faultOnNilArg0: true},
		{name: "LoweredAtomicLoad32", argLength: 2, reg: gpload, faultOnNilArg0: true},

		// 将arg1存储到arg0。arg2=mem。返回内存。
		// 同步
		// MOV（B|W）Rarg1，（Rarg0）
		// 同步
		{name: "LoweredAtomicStore8", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
		{name: "LoweredAtomicStore32", argLength: 3, reg: gpstore, faultOnNilArg0: true, hasSideEffects: true},
		{name: "LoweredAtomicStorezero", argLength: 2, reg: gpstore0, faultOnNilArg0: true, hasSideEffects: true},

		// 原子交换。
		// 将arg1存储到arg0。arg2=mem。返回<old content of*arg0，memory>。
		// 同步
		// LL（Rarg0），路由器
		// MOVW Rarg1，Rtmp
		// SC Rtmp（Rarg0）
		// BEQ Rtmp，-3（件）
		// 同步
		{name: "LoweredAtomicExchange", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},

		// 原子加法。
		// *arg0+=arg1。arg2=mem。返回<新的*arg0内容，内存>。
		// 同步
		// LL（Rarg0），路由器
		// 地址Rarg1，路由，Rtmp
		// SC Rtmp（Rarg0）
		// BEQ Rtmp，-3（件）
		// 同步
		// 地址：Rarg1，路由器
		{name: "LoweredAtomicAdd", argLength: 3, reg: gpxchg, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
		{name: "LoweredAtomicAddconst", argLength: 2, reg: regInfo{inputs: []regMask{gpspsbg}, outputs: []regMask{gp}}, aux: "Int32", resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},

		// 原子比较和交换。
		// arg0=指针，arg1=旧值，arg2=新值，arg3=内存。
		// 如果*arg0==arg1{
		// *arg0=arg2
		// 返回（真，内存）
		// }否则{
		// 返回（假，内存）
		// }
		// 同步
		// MOVW$0，溃败
		// LL（Rarg0），Rtmp
		// BNE Rtmp，Rarg1，4（件）
		// MOVW Rarg2，路由器
		// SC路由（Rarg0）
		// BEQ线路-4（PC）
		// 同步
		{name: "LoweredAtomicCas", argLength: 4, reg: gpcas, resultNotInArgs: true, faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},

		// 原子和/或。
		// *arg0&=（|=）arg1。arg2=mem。返回内存。
		// 同步
		// LL（Rarg0），Rtmp
		// 和Rarg1，Rtmp
		// SC Rtmp（Rarg0）
		// BEQ Rtmp，-3（件）
		// 同步
		{name: "LoweredAtomicAnd", argLength: 3, reg: gpstore, asm: "AND", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},
		{name: "LoweredAtomicOr", argLength: 3, reg: gpstore, asm: "OR", faultOnNilArg0: true, hasSideEffects: true, unsafePoint: true},

		// 大的或未对齐的归零
		// arg0=内存地址为零（在R1中，因副作用而更改）
		// arg1=最后一个元素的地址为零
		// arg2=mem
		// 生长素=排列
		// 返回mem
		// 苏布$4，R1
		// MOVW R0，4（R1）
		// 加4美元，R1
		// BNE Rarg1，R1，-2（PC）
		{
			name:      "LoweredZero",
			aux:       "Int32",
			argLength: 3,
			reg: regInfo{
				inputs:   []regMask{buildReg("R1"), gp},
				clobbers: buildReg("R1"),
			},
			faultOnNilArg0: true,
		},

		// 大的或未对齐的移动
		// arg0=dst内存地址（在R2中，因副作用而更改）
		// arg1=src内存地址（在R1中，因副作用而更改）
		// arg2=src最后一个元素的地址
		// arg3=mem
		// 生长素=排列
		// 返回mem
		// 苏布$4，R1
		// MOVW 4（R1），Rtmp
		// MOVW Rtmp（R2）
		// 加4美元，R1
		// 加4美元，R2
		// BNE Rarg2，R1，-4（PC）
		{
			name:      "LoweredMove",
			aux:       "Int32",
			argLength: 4,
			reg: regInfo{
				inputs:   []regMask{buildReg("R2"), buildReg("R1"), gp},
				clobbers: buildReg("R1 R2"),
			},
			faultOnNilArg0: true,
			faultOnNilArg1: true,
		},

		// 伪操作
		{name: "LoweredNilCheck", argLength: 2, reg: regInfo{inputs: []regMask{gpg}}, nilCheck: true, faultOnNilArg0: true}, // 如果arg0为零，则出现恐慌。arg1=mem。

		{name: "FPFlagTrue", argLength: 1, reg: readflags},  // bool，如果FP标志为true，则为true
		{name: "FPFlagFalse", argLength: 1, reg: readflags}, // bool，如果FP标志为false，则为true

		// 调度器确保LoweredGetClosurePtr仅在入口块中发生，
		// 并将其排序到块的最开始，以防止其他
		// R22（mips.REGCTXT，闭包指针）的使用
		{name: "LoweredGetClosurePtr", reg: regInfo{outputs: []regMask{buildReg("R22")}}, zeroWidth: true},

		// LoweredGetCallerSP返回当前函数调用方的SP。
		{name: "LoweredGetCallerSP", reg: gp01, rematerializeable: true},

		// LoweredGetCallerPC计算其“调用者”将返回的PC。
		// 也就是说，如果f调用g“calls”getcallerpc，
		// 结果应为g将返回到的f内的PC。
		// 有关更详细的讨论，请参阅runtime/stubs.go。
		{name: "LoweredGetCallerPC", reg: gp01, rematerializeable: true},

		// LoweredWB调用runtime.gcWriteBarrier。arg0=destptr，arg1=srcptr，arg2=mem，aux=runtime.gcWriteBarrier
		// 如有必要，它会保存所有GP寄存器，
		// 但击倒了R31（LR），因为这是一个电话
		// 和R23(REGTMP)。
		{name: "LoweredWB", argLength: 3, reg: regInfo{inputs: []regMask{buildReg("R20"), buildReg("R21")}, clobbers: (callerSave &^ gpg) | buildReg("R31")}, clobberFlags: true, aux: "Sym", symEffect: "None"},

		// 这些函数有三个，因此它们可以有三个不同的寄存器输入。
		// 当我们检查0<=c<=cap（A），然后检查0<=b<=c（b），然后检查0<=A<=b（c），我们需要
		// 要匹配的默认寄存器，因此我们不需要不必要地复制寄存器。
		{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r3, r4}}, typ: "Mem", call: true}, // arg0=idx，arg1=len，arg2=mem，返回内存。AuxInt包含报告代码（参见genericOps.go中的PanicBounds）。
		{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx，arg1=len，arg2=mem，返回内存。AuxInt包含报告代码（参见genericOps.go中的PanicBounds）。
		{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx，arg1=len，arg2=mem，返回内存。AuxInt包含报告代码（参见genericOps.go中的PanicBounds）。
		// 扩展操作与边界操作相同，只是索引是64位的。
		{name: "LoweredPanicExtendA", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r3, r4}}, typ: "Mem", call: true}, // arg0=idxHi，arg1=idxLo，arg2=len，arg3=mem，返回内存。AuxInt包含报告代码（参见genericOps.go中的PanicExtend）。
		{name: "LoweredPanicExtendB", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r2, r3}}, typ: "Mem", call: true}, // arg0=idxHi，arg1=idxLo，arg2=len，arg3=mem，返回内存。AuxInt包含报告代码（参见genericOps.go中的PanicExtend）。
		{name: "LoweredPanicExtendC", argLength: 4, aux: "Int64", reg: regInfo{inputs: []regMask{r5, r1, r2}}, typ: "Mem", call: true}, // arg0=idxHi，arg1=idxLo，arg2=len，arg3=mem，返回内存。AuxInt包含报告代码（参见genericOps.go中的PanicExtend）。
	}

	blocks := []blockData{
		{name: "EQ", controls: 1},
		{name: "NE", controls: 1},
		{name: "LTZ", controls: 1}, // < 0
		{name: "LEZ", controls: 1}, // <= 0
		{name: "GTZ", controls: 1}, // > 0
		{name: "GEZ", controls: 1}, // >= 0
		{name: "FPT", controls: 1}, // FP标志为真
		{name: "FPF", controls: 1}, // FP标志为false
	}

	archs = append(archs, arch{
		name:            "MIPS",
		pkg:             "cmd/internal/obj/mips",
		genfile:         "../../mips/ssa.go",
		ops:             ops,
		blocks:          blocks,
		regnames:        regNamesMIPS,
		gpregmask:       gp,
		fpregmask:       fp,
		specialregmask:  hi | lo,
		framepointerreg: -1, // 不用
		linkreg:         int8(num["R31"]),
	})
}
